package se.webbzon.boltzmann.collector;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;

import se.webbzon.boltzmann.debug.Debug;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldarea.WorldAreaListener;
import se.webbzon.oschi01.worldobject.WorldObject;

public class Collector<T extends WorldObject> extends WorldObject {
	
	// The class of the world object to collect
	private final Class<T> clazz;
	
	// A collection of world objects
	private final Collection<T> instances;
	
	// A collection of all the listeners to this collector
	private final Collection<CollectorListener<T>> listeners;
	
	// A listener to the world area
	private final WorldAreaListener worldAreaListener;
	
	/*============================================================
	Constructors
	============================================================*/

	private Collector(Class<T> clazz) {
		super(WorldObject.onCreate + WorldObject.onDestroy);
		this.clazz = clazz;
		instances = Collections.synchronizedCollection(new HashSet<T>());
		listeners = new ArrayList<CollectorListener<T>>();
		worldAreaListener = new WorldAreaListenerImpl();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final <T extends WorldObject> Collector<T> getCollector(WorldArea world, Class<T> clazz) {
		Collector<T> c = null;
		Collection<Collector> collectors = world.findInstances(Collector.class);
		for (Collector collector : collectors)
			if (collector.getInstanceClass() == clazz) {
				c = collector;
				break;
			}
		if (c == null) {
			c = new Collector<T>(clazz);
			world.createInstance(c);
		}
		return c;
	}
	
	/** Returns the class of the world object which is collected by this collector. **/
	public Class<T> getInstanceClass() {
		return clazz;
	}
	
	/** Returns a synchronized collection of all the instances collected by
	 * this collector. **/
	public Collection<T> getInstances() {
		return Collections.unmodifiableCollection(instances);
	}
	
	/** Adds a new listener to this collector. **/
	public boolean addListener(CollectorListener<T> listener) {
		int d;
		Debug.debugCollection(this, listeners);
		return listeners.add(listener);
	}
	
	/** Removes a listener from this collector. **/
	public boolean removeListener(CollectorListener<T> listener) {
		return listeners.remove(listener);
	}
	
	/** Clears all the listeners from this collector. **/
	public void clearListener() {
		listeners.clear();
	}
	
	@Override public void onCreate(WorldArea world) {
		instances.addAll(world.findInstances(clazz));
		world.addListener(worldAreaListener);
	}
	
	@Override public void onDestroy(WorldArea world) {
		for (CollectorListener<T> listener : listeners)
			listener.onDestroyCollector(this);
		world.removeListener(worldAreaListener);
		instances.clear();
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** An implementation of a world area listener. **/
	private final class WorldAreaListenerImpl implements WorldAreaListener {

		@SuppressWarnings("unchecked")
		@Override public void onInstanceAdd(WorldArea source, WorldObject instance) {
			if (clazz.isInstance(instance)) {
				T inst = (T) instance;
				int d;
				Debug.debugCollection(this, instances);
				instances.add(inst);
				for (CollectorListener<T> listener : listeners)
					listener.onCreateInstance(Collector.this, inst);
			}
		}

		@SuppressWarnings("unchecked")
		@Override public void onInstanceRemove(WorldArea source, WorldObject instance) {
			if (clazz.isInstance(instance)) {
				instances.remove(instance);
				for (CollectorListener<T> listener : listeners)
					listener.onDestroyInstance(Collector.this, (T) instance);
			}
		}

		@Override public void onGoto(WorldArea source, WorldArea destination) {}

		@Override public void onInstancesClear(WorldArea source) {
			instances.clear();
			int w;
		};
		
	}

}
